package net.helix.core.geometry;

import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;

import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.ARBVertexBufferObject;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GLContext;
import org.lwjgl.util.glu.Sphere;
import org.lwjgl.util.vector.Vector3f;

import net.helix.core.Bounds;
import net.helix.core.Face;
import net.helix.core.Normal;
import net.helix.core.UV;
import net.helix.core.Vertex;
import net.helix.graphics.GraphicsHelper;

/**
 * Warning, a mesh in Helix works a little bit differently than most
 * other programs do. Meshes will ONLY contain material and face data.
 * The vertices and uv coordinates and vertex normals are contained in
 * the Geometry class. The Mesh class is given a reference to the list
 * of vertices, uvs, and vertex normals however.
 * @author Runouw
 *
 */
public class Geometry_Mesh {
	private ArrayList<Face> faces = new ArrayList<Face>(); 
	
	//private boolean useNormals = false;
	private boolean prepared = false;
	
	private int dataVboId = -1;
	private int indexVboId = -1;
	
	
	public Bounds bounds = new Bounds();
	public Bounds getBounds(){
		return bounds;
	}
	
	
	public void setFaces(ArrayList<Face> faces){
		this.faces = faces;
		
	}
	public ArrayList<Face> getFaces(){
		return faces;
		
		
	}
	
	public void setShadingSmooth(Geometry_Model model, Geometry_Material material){
		// TODO: check all vertex normals and remove unused ones...
		
		Vector3f cross = new Vector3f();
		for(int i=0;i<faces.size();i++){
			Face face = faces.get(i);
			
			if(face.v1 < 0) return;
			if(face.v2 < 0) return;
			if(face.v3 < 0) return;
			
			// calculate normal to face...
			Vertex vertex1 = model.vertices.get(face.v1);
			Vertex vertex2 = model.vertices.get(face.v2);
			Vertex vertex3 = model.vertices.get(face.v3);
			
			Vector3f l1 = new Vector3f(vertex2.getX() - vertex1.getX(), vertex2.getY() - vertex1.getY(), vertex2.getZ() - vertex1.getZ());
			Vector3f l2 = new Vector3f(vertex3.getX() - vertex1.getX(), vertex3.getY() - vertex1.getY(), vertex3.getZ() - vertex1.getZ());
			
			Vector3f.cross(l1, l2, cross);
			if(cross.length() > 0){
				cross.normalise();
			}
			
			//Normal normal1 = normals.get(face.v1);
			//Normal normal2 = normals.get(face.v2);
			//Normal normal3 = normals.get(face.v3);
			
			vertex1.addVertexNormals(cross.x, cross.y, cross.z);
			
			vertex2.addVertexNormals(cross.x, cross.y, cross.z);
			
			vertex3.addVertexNormals(cross.x, cross.y, cross.z);
		}
		
		
		
		/*
		for(int i=0;i<faces.size();i++){
			Face face = faces.get(i);
			
			Vertex vertex1 = model.vertices.get(face.v1);
			Vertex vertex2 = model.vertices.get(face.v2);
			Vertex vertex3 = model.vertices.get(face.v3);
			
			
			face.vn1 = model.normals.size();
			model.normals.add(new Normal(vertex1.getVX(), vertex1.getVY(), vertex1.getVZ()));
			
			face.vn2 = model.normals.size();
			model.normals.add(new Normal(vertex2.getVX(), vertex2.getVY(), vertex2.getVZ()));
			
			face.vn3 = model.normals.size();
			model.normals.add(new Normal(vertex3.getVX(), vertex3.getVY(), vertex3.getVZ()));
			
		}
		*/
		
		material.useNormals = true;
	}
	public void setShadingFlat(Geometry_Model model, Geometry_Material material){
		// TODO: check all vertex normals and remove unused ones...
		
		for(int i=0;i<faces.size();i++){
			Face face = faces.get(i);
			
			// calculate normal to face...
			Vertex vertex1 = model.vertices.get(face.v1);
			Vertex vertex2 = model.vertices.get(face.v2);
			Vertex vertex3 = model.vertices.get(face.v3);
			
			Vector3f l1 = new Vector3f(vertex2.getX() - vertex1.getX(), vertex2.getY() - vertex1.getY(), vertex2.getZ() - vertex1.getZ());
			Vector3f l2 = new Vector3f(vertex3.getX() - vertex1.getX(), vertex3.getY() - vertex1.getY(), vertex3.getZ() - vertex1.getZ());
			
			Vector3f cross = Vector3f.cross(l1, l2, null);
			cross.normalise();
			
			face.vn1 = model.normals.size();
			model.normals.add(new Normal(cross.x, cross.y, cross.z));
			
			face.vn2 = model.normals.size();
			model.normals.add(new Normal(cross.x, cross.y, cross.z));
			
			face.vn3 = model.normals.size();
			model.normals.add(new Normal(cross.x, cross.y, cross.z));
		}
		// TODO: check all vertex normals and remove unused ones...
		
		material.useNormals = true;
	}
	public void prepare(Geometry_Model model){
		bounds.clear();
		for(int i=0;i<faces.size();i++){
			Face face = faces.get(i);
			
			// calculate normal to face...
			Vertex vertex1 = model.vertices.get(face.v1);
			Vertex vertex2 = model.vertices.get(face.v2);
			Vertex vertex3 = model.vertices.get(face.v3);
			
			bounds.consider(vertex1.getX(), vertex1.getY(), vertex1.getZ());
			bounds.consider(vertex2.getX(), vertex2.getY(), vertex2.getZ());
			bounds.consider(vertex3.getX(), vertex3.getY(), vertex3.getZ());
			
		}
		
	}
	int verticeSize = 3*4;
	int normalSize = 3*4;
	int texSize = 2*4;
	int elementSize;
	renderBufferType buffType = null;
	enum renderBufferType{
		ALL, VERT_ONLY, VERT_UVS, VERT_NORMAL, NONE
	};
	
	public void prepareRender(Geometry_Model model, Geometry_Material material){
		System.out.println("Prepare model");
		
		buffType = null;
		
		if(dataVboId != -1){
			// TODO: delete old buffers
		}
		
		dataVboId = -1;
		indexVboId = -1;
		
		// TODO: there is definitely a way to optimize this... when there are shared vertices...
		if (GLContext.getCapabilities().GL_ARB_vertex_buffer_object) {
			boolean uvs = false;
			boolean normals = false;
			
			if(checkIfUVs(model)){
				uvs = true;
			}
			if(checkIfNormals(model) || material.useNormals){
				normals = true;
			}
			
			if(uvs && normals){
				buffType = renderBufferType.ALL;
			}else if(uvs && !normals){
				buffType = renderBufferType.VERT_UVS;
			}else if(!uvs && normals){
				buffType = renderBufferType.VERT_NORMAL;
			}else{
				buffType = renderBufferType.VERT_ONLY;
			}
			
			
			if(material != null){
				
				System.out.println("Using vertex buffers");
				
				if(buffType == renderBufferType.ALL){
					
					elementSize = verticeSize+normalSize+texSize;
				
					dataVboId = ARBVertexBufferObject.glGenBuffersARB();
					indexVboId = ARBVertexBufferObject.glGenBuffersARB();
					
					ARBVertexBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, dataVboId);
					ARBVertexBufferObject.glBufferDataARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, (elementSize * faces.size() * 3), ARBVertexBufferObject.GL_STATIC_DRAW_ARB);
					
					ByteBuffer dataBuffer = ARBVertexBufferObject.glMapBufferARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, ARBVertexBufferObject.GL_WRITE_ONLY_ARB, (elementSize * faces.size() * 3), null);
					
					
					
					for(int i=0;i<faces.size();i++){
						Face f = faces.get(i);
	
						try{
							Vertex v1 = model.vertices.get(f.v1);
							Vertex v2 = model.vertices.get(f.v2);
							Vertex v3 = model.vertices.get(f.v3);
							
							UV vt1 = model.uvs.get(f.vt1);
							UV vt2 = model.uvs.get(f.vt2);
							UV vt3 = model.uvs.get(f.vt3);
							
							dataBuffer.putFloat(v1.getX()); dataBuffer.putFloat(v1.getY()); dataBuffer.putFloat(v1.getZ());
							dataBuffer.putFloat(v1.getVX()); dataBuffer.putFloat(v1.getVY()); dataBuffer.putFloat(v1.getVZ());
							dataBuffer.putFloat(vt1.getUvx()); dataBuffer.putFloat(1-vt1.getUvy());
							
							dataBuffer.putFloat(v2.getX()); dataBuffer.putFloat(v2.getY()); dataBuffer.putFloat(v2.getZ());
							dataBuffer.putFloat(v2.getVX()); dataBuffer.putFloat(v2.getVY()); dataBuffer.putFloat(v2.getVZ());
							dataBuffer.putFloat(vt2.getUvx()); dataBuffer.putFloat(1-vt2.getUvy());
							
							dataBuffer.putFloat(v3.getX()); dataBuffer.putFloat(v3.getY()); dataBuffer.putFloat(v3.getZ());
							dataBuffer.putFloat(v3.getVX()); dataBuffer.putFloat(v3.getVY()); dataBuffer.putFloat(v3.getVZ());
							dataBuffer.putFloat(vt3.getUvx()); dataBuffer.putFloat(1-vt3.getUvy());
							
						}catch(Exception e){
							e.printStackTrace();
						}
					}
					
					dataBuffer.flip();
					
					//unmap and unbind
					ARBVertexBufferObject.glUnmapBufferARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB);
					ARBVertexBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, 0);
					
					//Allocate the index buffer for three elements (they will point to the three elements above)
					ARBVertexBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB, indexVboId);
					ARBVertexBufferObject.glBufferDataARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB, 4 * faces.size()*3, ARBVertexBufferObject.GL_STATIC_DRAW_ARB);
					
					//Map the index buffer to a ByteBuffer
					ByteBuffer indexBuffer = ARBVertexBufferObject.glMapBufferARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB, ARBVertexBufferObject.GL_WRITE_ONLY_ARB, 4 * faces.size()*3, null);
					
					for(int i=0;i<faces.size();i++){
						indexBuffer.putInt(i*3);
						indexBuffer.putInt(i*3 + 1);
						indexBuffer.putInt(i*3 + 2);
					}
					indexBuffer.flip();
					
					ARBVertexBufferObject.glUnmapBufferARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB);
					ARBVertexBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
					
				}
				if(buffType == renderBufferType.VERT_NORMAL){
					
					elementSize = verticeSize+normalSize;
				
					dataVboId = ARBVertexBufferObject.glGenBuffersARB();
					indexVboId = ARBVertexBufferObject.glGenBuffersARB();
					
					ARBVertexBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, dataVboId);
					ARBVertexBufferObject.glBufferDataARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, (elementSize * faces.size() * 3), ARBVertexBufferObject.GL_STATIC_DRAW_ARB);
					
					ByteBuffer dataBuffer = ARBVertexBufferObject.glMapBufferARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, ARBVertexBufferObject.GL_WRITE_ONLY_ARB, (elementSize * faces.size() * 3), null);
					
					
					
					for(int i=0;i<faces.size();i++){
						Face f = faces.get(i);
	
						try{
							Vertex v1 = model.vertices.get(f.v1);
							Vertex v2 = model.vertices.get(f.v2);
							Vertex v3 = model.vertices.get(f.v3);
							
							dataBuffer.putFloat(v1.getX()); dataBuffer.putFloat(v1.getY()); dataBuffer.putFloat(v1.getZ());
							dataBuffer.putFloat(v1.getVX()); dataBuffer.putFloat(v1.getVY()); dataBuffer.putFloat(v1.getVZ());
							
							dataBuffer.putFloat(v2.getX()); dataBuffer.putFloat(v2.getY()); dataBuffer.putFloat(v2.getZ());
							dataBuffer.putFloat(v2.getVX()); dataBuffer.putFloat(v2.getVY()); dataBuffer.putFloat(v2.getVZ());
							
							dataBuffer.putFloat(v3.getX()); dataBuffer.putFloat(v3.getY()); dataBuffer.putFloat(v3.getZ());
							dataBuffer.putFloat(v3.getVX()); dataBuffer.putFloat(v3.getVY()); dataBuffer.putFloat(v3.getVZ());
							
						}catch(Exception e){
							e.printStackTrace();
						}
					}
					
					dataBuffer.flip();
					
					//unmap and unbind
					ARBVertexBufferObject.glUnmapBufferARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB);
					ARBVertexBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, 0);
					
					//Allocate the index buffer for three elements (they will point to the three elements above)
					ARBVertexBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB, indexVboId);
					ARBVertexBufferObject.glBufferDataARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB, 4 * faces.size()*3, ARBVertexBufferObject.GL_STATIC_DRAW_ARB);
					
					//Map the index buffer to a ByteBuffer
					ByteBuffer indexBuffer = ARBVertexBufferObject.glMapBufferARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB, ARBVertexBufferObject.GL_WRITE_ONLY_ARB, 4 * faces.size()*3, null);
					
					for(int i=0;i<faces.size();i++){
						indexBuffer.putInt(i*3);
						indexBuffer.putInt(i*3 + 1);
						indexBuffer.putInt(i*3 + 2);
					}
					indexBuffer.flip();
					
					ARBVertexBufferObject.glUnmapBufferARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB);
					ARBVertexBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
					
				}
				
				if(buffType == renderBufferType.VERT_UVS){
					
					elementSize = verticeSize+texSize;
				
					dataVboId = ARBVertexBufferObject.glGenBuffersARB();
					indexVboId = ARBVertexBufferObject.glGenBuffersARB();
					
					ARBVertexBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, dataVboId);
					ARBVertexBufferObject.glBufferDataARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, (elementSize * faces.size() * 3), ARBVertexBufferObject.GL_STATIC_DRAW_ARB);
					
					ByteBuffer dataBuffer = ARBVertexBufferObject.glMapBufferARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, ARBVertexBufferObject.GL_WRITE_ONLY_ARB, (elementSize * faces.size() * 3), null);
					
					
					
					for(int i=0;i<faces.size();i++){
						Face f = faces.get(i);
	
						try{
							Vertex v1 = model.vertices.get(f.v1);
							Vertex v2 = model.vertices.get(f.v2);
							Vertex v3 = model.vertices.get(f.v3);
							
							UV vt1 = model.uvs.get(f.vt1);
							UV vt2 = model.uvs.get(f.vt2);
							UV vt3 = model.uvs.get(f.vt3);
							
							dataBuffer.putFloat(v1.getX()); dataBuffer.putFloat(v1.getY()); dataBuffer.putFloat(v1.getZ());
							dataBuffer.putFloat(vt1.getUvx()); dataBuffer.putFloat(1-vt1.getUvy());
							
							dataBuffer.putFloat(v2.getX()); dataBuffer.putFloat(v2.getY()); dataBuffer.putFloat(v2.getZ());
							dataBuffer.putFloat(vt2.getUvx()); dataBuffer.putFloat(1-vt2.getUvy());
							
							dataBuffer.putFloat(v3.getX()); dataBuffer.putFloat(v3.getY()); dataBuffer.putFloat(v3.getZ());
							dataBuffer.putFloat(vt3.getUvx()); dataBuffer.putFloat(1-vt3.getUvy());
						}catch(Exception e){
							e.printStackTrace();
						}
					}
					
					dataBuffer.flip();
					
					//unmap and unbind
					ARBVertexBufferObject.glUnmapBufferARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB);
					ARBVertexBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, 0);
					
					//Allocate the index buffer for three elements (they will point to the three elements above)
					ARBVertexBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB, indexVboId);
					ARBVertexBufferObject.glBufferDataARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB, 4 * faces.size()*3, ARBVertexBufferObject.GL_STATIC_DRAW_ARB);
					
					//Map the index buffer to a ByteBuffer
					ByteBuffer indexBuffer = ARBVertexBufferObject.glMapBufferARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB, ARBVertexBufferObject.GL_WRITE_ONLY_ARB, 4 * faces.size()*3, null);
					
					for(int i=0;i<faces.size();i++){
						indexBuffer.putInt(i*3);
						indexBuffer.putInt(i*3 + 1);
						indexBuffer.putInt(i*3 + 2);
					}
					indexBuffer.flip();
					
					ARBVertexBufferObject.glUnmapBufferARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB);
					ARBVertexBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
					
				}
				
				if(buffType == renderBufferType.VERT_ONLY){
					
					elementSize = verticeSize;
					
					dataVboId = ARBVertexBufferObject.glGenBuffersARB();
					indexVboId = ARBVertexBufferObject.glGenBuffersARB();
					
					ARBVertexBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, dataVboId);
					ARBVertexBufferObject.glBufferDataARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, (elementSize * faces.size() * 3), ARBVertexBufferObject.GL_STATIC_DRAW_ARB);
					
					ByteBuffer dataBuffer = ARBVertexBufferObject.glMapBufferARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, ARBVertexBufferObject.GL_WRITE_ONLY_ARB, (elementSize * faces.size() * 3), null);
					
					for(int i=0;i<faces.size();i++){
						Face f = faces.get(i);
	
						try{
							Vertex v1 = model.vertices.get(f.v1);
							Vertex v2 = model.vertices.get(f.v2);
							Vertex v3 = model.vertices.get(f.v3);
							
							dataBuffer.putFloat(v1.getX()); dataBuffer.putFloat(v1.getY()); dataBuffer.putFloat(v1.getZ());
							
							dataBuffer.putFloat(v2.getX()); dataBuffer.putFloat(v2.getY()); dataBuffer.putFloat(v2.getZ());

							dataBuffer.putFloat(v3.getX()); dataBuffer.putFloat(v3.getY()); dataBuffer.putFloat(v3.getZ());
						}catch(Exception e){
							e.printStackTrace();
						}
					}
					
					dataBuffer.flip();
					
					//unmap and unbind
					ARBVertexBufferObject.glUnmapBufferARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB);
					ARBVertexBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, 0);
					
					//Allocate the index buffer for three elements (they will point to the three elements above)
					ARBVertexBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB, indexVboId);
					ARBVertexBufferObject.glBufferDataARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB, 4 * faces.size()*3, ARBVertexBufferObject.GL_STATIC_DRAW_ARB);
					
					//Map the index buffer to a ByteBuffer
					ByteBuffer indexBuffer = ARBVertexBufferObject.glMapBufferARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB, ARBVertexBufferObject.GL_WRITE_ONLY_ARB, 4 * faces.size()*3, null);
					
					for(int i=0;i<faces.size();i++){
						indexBuffer.putInt(i*3);
						indexBuffer.putInt(i*3 + 1);
						indexBuffer.putInt(i*3 + 2);
					}
					indexBuffer.flip();
					
					ARBVertexBufferObject.glUnmapBufferARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB);
					ARBVertexBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
					
				}
				
			}else{
				buffType = renderBufferType.NONE;
			}
		}else{
			buffType = renderBufferType.NONE;
		}
		
		System.out.println("buffType = "+buffType);
		System.out.println("Finished preparing model");
		
	}
	private boolean checkIfUVs(Geometry_Model model){
		for(int i=0;i<faces.size();i++){
			Face f = faces.get(i);
			
			if(f.vt1 == -1 || f.vt2 == -1 || f.vt3 == -1){
				return false;
			}
			
			if(model.uvs.size() <= f.vt1 || model.uvs.size() <= f.vt2 || model.uvs.size() <= f.vt2){
				return false;
			}
		}
		return true;
	}
	private boolean checkIfNormals(Geometry_Model model){
		for(int i=0;i<faces.size();i++){
			Face f = faces.get(i);
			
			if(f.vn1 == -1 || f.vn2 == -1 || f.vn3 == -1){
				return false;
			}
			
			if(model.normals.size() <= f.vn1 || model.normals.size() <= f.vn2 || model.normals.size() <= f.vn2){
				return false;
			}
		}
		return true;
	}
	static Sphere sphere = new Sphere();
	public void render(Geometry_Model model, Geometry_Material material) {
		// TODO: check frustrum culling
		
		if(prepared == false){
			// TODO: remember that you need to prepare() again
			// if you change the material or shading!
			prepareRender(model, material);
			prepared = true;
		}
		
		float w = bounds.getWidth();
		float h = bounds.getHeight();
		float d = bounds.getDepth();
		float r = (float) (Math.max(Math.max(w,  h), d)*Math.sqrt(2)/2);
		
		if(GraphicsHelper.isSphereInFrustrum(bounds.getMiddleX(), bounds.getMiddleY(), bounds.getMiddleZ(), r)){
			
			if(material != null){
				material.bind();
			}
			
			if(buffType != renderBufferType.NONE && indexVboId != -1 && dataVboId != -1){
				
				if(buffType == renderBufferType.ALL){
					GL11.glEnableClientState(GL11.GL_VERTEX_ARRAY);
					GL11.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
					GL11.glEnableClientState(GL11.GL_NORMAL_ARRAY);
		
					//bind the vertex buffer and the index buffer
					ARBVertexBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, dataVboId);
					ARBVertexBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB, indexVboId);
					
					//the vertex pointer has an offset of 0 and a stride of elementSize
					GL11.glVertexPointer(3, GL11.GL_FLOAT, elementSize, 0);
					//the normal pointer has an offset of verticeSize and a stride of elementSize
					GL11.glNormalPointer(GL11.GL_FLOAT, elementSize, verticeSize);
					//the color pointer has an offset of (verticeSize+normalSize) and a stride of elementSize
					GL11.glTexCoordPointer(2, GL11.GL_FLOAT, elementSize, verticeSize+normalSize);
					
					//Draw the triangle with three indexes (with 0 offset in the index buffer)
					GL11.glDrawElements(GL11.GL_TRIANGLES, faces.size()*3, GL11.GL_UNSIGNED_INT, 0);
					
					//unbind the buffers
					ARBVertexBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, 0);
					ARBVertexBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
					
					
					GL11.glDisableClientState(GL11.GL_VERTEX_ARRAY);
					GL11.glDisableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
					GL11.glDisableClientState(GL11.GL_NORMAL_ARRAY);
					
				}
				if(buffType == renderBufferType.VERT_NORMAL){
					GL11.glEnableClientState(GL11.GL_VERTEX_ARRAY);
					GL11.glEnableClientState(GL11.GL_NORMAL_ARRAY);
		
					//bind the vertex buffer and the index buffer
					ARBVertexBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, dataVboId);
					ARBVertexBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB, indexVboId);
					
					//the vertex pointer has an offset of 0 and a stride of elementSize
					GL11.glVertexPointer(3, GL11.GL_FLOAT, elementSize, 0);
					//the normal pointer has an offset of verticeSize and a stride of elementSize
					GL11.glNormalPointer(GL11.GL_FLOAT, elementSize, verticeSize);
					
					//Draw the triangle with three indexes (with 0 offset in the index buffer)
					GL11.glDrawElements(GL11.GL_TRIANGLES, faces.size()*3, GL11.GL_UNSIGNED_INT, 0);
					
					//unbind the buffers
					ARBVertexBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, 0);
					ARBVertexBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
					
					
					GL11.glDisableClientState(GL11.GL_VERTEX_ARRAY);
					GL11.glDisableClientState(GL11.GL_NORMAL_ARRAY);
					
				}
				if(buffType == renderBufferType.VERT_UVS){
					GL11.glEnableClientState(GL11.GL_VERTEX_ARRAY);
					GL11.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
		
					//bind the vertex buffer and the index buffer
					ARBVertexBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, dataVboId);
					ARBVertexBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB, indexVboId);
					
					//the vertex pointer has an offset of 0 and a stride of elementSize
					GL11.glVertexPointer(3, GL11.GL_FLOAT, elementSize, 0);
					//the color pointer has an offset of (verticeSize+normalSize) and a stride of elementSize
					GL11.glTexCoordPointer(2, GL11.GL_FLOAT, elementSize, verticeSize);
					
					//Draw the triangle with three indexes (with 0 offset in the index buffer)
					GL11.glDrawElements(GL11.GL_TRIANGLES, faces.size()*3, GL11.GL_UNSIGNED_INT, 0);
					
					//unbind the buffers
					ARBVertexBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, 0);
					ARBVertexBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
					
					
					GL11.glDisableClientState(GL11.GL_VERTEX_ARRAY);
					GL11.glDisableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
					
				}
				if(buffType == renderBufferType.VERT_ONLY){
					GL11.glEnableClientState(GL11.GL_VERTEX_ARRAY);
		
					//bind the vertex buffer and the index buffer
					ARBVertexBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, dataVboId);
					ARBVertexBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB, indexVboId);
					
					//the vertex pointer has an offset of 0 and a stride of elementSize
					GL11.glVertexPointer(3, GL11.GL_FLOAT, elementSize, 0);
					//the normal pointer has an offset of verticeSize and a stride of elementSize
					
					//Draw the triangle with three indexes (with 0 offset in the index buffer)
					GL11.glDrawElements(GL11.GL_TRIANGLES, faces.size()*3, GL11.GL_UNSIGNED_INT, 0);
					
					//unbind the buffers
					ARBVertexBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, 0);
					ARBVertexBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
					
					
					GL11.glDisableClientState(GL11.GL_VERTEX_ARRAY);
					
				}
				
			}else{
				oldRender(model, material);
			}
			
			if(material != null){
				material.unbind();
			}
			
		}
		
	}
	public void renderSphere(Geometry_Model model, Geometry_Material material){
		float w = bounds.getWidth();
		float h = bounds.getHeight();
		float d = bounds.getDepth();
		float r = (float) (Math.max(Math.max(w,  h), d)*Math.sqrt(2)/2);
		
		if(GraphicsHelper.isSphereInFrustrum(bounds.getMiddleX(), bounds.getMiddleY(), bounds.getMiddleZ(), r)){
		
		
			GL11.glColor4f(.5f, .5f, .5f, .2f);
		}else{
			GL11.glColor4f(1f, .5f, .5f, .2f);
		}
		
		
		GL11.glEnable(GL11.GL_BLEND);
		
		GL11.glTranslatef(bounds.getMiddleX(), bounds.getMiddleY(), bounds.getMiddleZ());
		sphere.draw(r, 10, 10);
		GL11.glTranslatef(-bounds.getMiddleX(), -bounds.getMiddleY(), -bounds.getMiddleZ());
		
		
	}
	public void renderBoudingBox(){
		GL11.glPushMatrix();
		
		GL11.glTranslatef(bounds.getMiddleX(), bounds.getMiddleY(), bounds.getMiddleZ());
		
		GL11.glScalef(bounds.getWidth()/2, bounds.getHeight()/2, bounds.getDepth()/2);
		
		GL11.glLineWidth(1f);
		GL11.glColor4f(.5f, .5f, .5f, 1f);
		GraphicsHelper.drawDube();
		
		
		GL11.glPopMatrix();
	}
	
	private void oldRender(Geometry_Model model, Geometry_Material material){
		// make a list and upload stuff to buffers
		GL11.glBegin(GL11.GL_TRIANGLES);
		for(int i=0;i<faces.size();i++){
			Face f = faces.get(i);

			try{
				Vertex v1 = model.vertices.get(f.v1);
				Vertex v2 = model.vertices.get(f.v2);
				Vertex v3 = model.vertices.get(f.v3);

				if(material != null){
					if(model.normals.size() > 0 && material.useNormals){

						UV vt1 = model.uvs.get(f.vt1);
						UV vt2 = model.uvs.get(f.vt2);
						UV vt3 = model.uvs.get(f.vt3);

						//Normal vn1 = model.normals.get(f.vn1);
						//Normal vn2 = model.normals.get(f.vn2);
						//Normal vn3 = model.normals.get(f.vn3);

						GL11.glNormal3f(v1.getVX(), v1.getVY(), v1.getVZ()); GL11.glTexCoord2f(vt1.getUvx(), 1-vt1.getUvy()); GL11.glVertex3f(v1.getX(), v1.getY(), v1.getZ());
						GL11.glNormal3f(v2.getVX(), v2.getVY(), v2.getVZ()); GL11.glTexCoord2f(vt2.getUvx(), 1-vt2.getUvy()); GL11.glVertex3f(v2.getX(), v2.getY(), v2.getZ());
						GL11.glNormal3f(v3.getVX(), v3.getVY(), v3.getVZ()); GL11.glTexCoord2f(vt3.getUvx(), 1-vt3.getUvy()); GL11.glVertex3f(v3.getX(), v3.getY(), v3.getZ());
					}else{

						UV vt1 = model.uvs.get(f.vt1);
						UV vt2 = model.uvs.get(f.vt2);
						UV vt3 = model.uvs.get(f.vt3);

						GL11.glTexCoord2f(vt1.getUvx(), 1-vt1.getUvy()); GL11.glVertex3f(v1.getX(), v1.getY(), v1.getZ());
						GL11.glTexCoord2f(vt2.getUvx(), 1-vt2.getUvy()); GL11.glVertex3f(v2.getX(), v2.getY(), v2.getZ());
						GL11.glTexCoord2f(vt3.getUvx(), 1-vt3.getUvy()); GL11.glVertex3f(v3.getX(), v3.getY(), v3.getZ());

					}
				}else{
					GL11.glVertex3f(v1.getX(), v1.getY(), v1.getZ());
					GL11.glVertex3f(v2.getX(), v2.getY(), v2.getZ());
					GL11.glVertex3f(v3.getX(), v3.getY(), v3.getZ());
				}
			}catch(Exception e){

			}


		}

		GL11.glEnd();
	}
}
